using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Resources;

namespace Budgetor
{
	/// <summary>
	/// This control is used to draw Histogram.
	/// </summary>
	public partial class HistogramControl : UserControl
	{
		#region Private Members
		
		private int binCount = 25;
		private Color color = Color.Khaki;
		private int border = 20;
		bool IsDrawing = true;
		bool autoCalculate = true;
		double MinValue;
		double Interval;
		float binWidth;
		float yUnit;
		int BinMax;
		int YgridHeight = 40;
		string Xtext;
		string Ytext;
		string text;
		Rectangle rectBoundary;

		int[] histogram = new int[0];
		Rectangle rectBin;

		Font font;
		List<double> listPlotData = new List<double> ();
		ResourceManager objResourceManager = new ResourceManager ("FugroJason.GUI.Core.CoreControls.Histogram.HistogramControl.Resources",
			System.Reflection.Assembly.GetExecutingAssembly ());

		#endregion

		#region Constructor
		/// <summary>
		/// constructor
		/// </summary>
		public HistogramControl ( )
		{
			InitializeComponent ();

			// Enable double buffering
			this.SetStyle (
			 ControlStyles.AllPaintingInWmPaint |
			 ControlStyles.UserPaint |
			 ControlStyles.DoubleBuffer, true);
			font= new Font ("Tahoma", 8);

		}
		#endregion

		#region Public Properties
		/// <summary>
		/// gets or sets a value indicating number of bins of Histogram.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("Number of bins")]
		public int BinCount
		{
			get
			{
				return binCount;
			}
			set
			{
				if ( binCount < 1 )
					throw new ArgumentException ();
				binCount = value;
			}
		}
		/// <summary>
		/// Gets or sets color of Histogram.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("The color used within the control")]
		public Color DisplayColor
		{
			set
			{
				color = value;
				Invalidate ();
			}
			get
			{
				return color;
			}
		}
		/// <summary>
		/// Gets or sets a value indicating distance from the margins for the histogram.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("The distance from the margins for the histogram")]
		public int Border
		{
			set
			{
				if ( value > 0 )
					border = value;
			}
			get
			{
				return border;
			}
		}
		/// <summary>
		/// Gets or sets a value indicating Height of Y Grids.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("Height of Y Grids")]
		public int YGridHeight
		{
			set
			{
				YgridHeight = value;
			}
			get
			{
				return YgridHeight;
			}
		}
		/// <summary>
		/// Gets or sets value for display X Axis Label.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("Display X Axis Label")]
		public string XText
		{
			set
			{
				Xtext = value;
			}
			get
			{
				return Xtext;
			}
		}
		/// <summary>
		/// Gets or sets value for display Y Axis Label.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("Display Y Axis Label")]
		public string YText
		{
			set
			{
				Ytext = value;
			}
			get
			{
				return Ytext;
			}
		}
		/// <summary>
		/// Gets or sets value for display Title of the Histogram.
		/// </summary>
		[Category ("Histogram Options")]
		[Description ("Display Title of the Histogram")]
		public string Title
		{
			set
			{
				text = value;
			}
			get
			{
				return text;
			}
		}
		/// <summary>
		/// Gets or sets value indicating whether calculate Y Grid Height Automatically.
		/// </summary>
		/// <value><c>true</c> if [Auto calculate]; otherwise, <c>false</c>.</value>
		[Category ("Histogram Options")]
		[Description ("Option for Calculating YGridHeight")]
		public bool AutoCalculate
		{
			set
			{
				autoCalculate = value;
			}
			get
			{
				return autoCalculate;
			}
		}

		[Category ("Histogram Options")]
		[Description ("Histogram Data")]
		public List<double> Data
		{
			get
			{
				return listPlotData;
			}
			set
			{
				listPlotData = value;
				this.Invalidate ();
			}
		}

		#endregion		

		#region Protected Methods
		/// <summary>
		/// This method paints the HistogramControl.
		/// </summary>
		/// <param name="e">paint arguments use to paint the HistogramControl</param>
		protected override void OnPaint ( PaintEventArgs e )
		{
			base.OnPaint (e);
			Graphics graphics = e.Graphics;

			graphics.Clear (Color.Snow);
			graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

			//Histogram Calculations.
			CalculateHistogram ();


			Pen rectPen = new Pen (Color.Black);
			Font fontTahoma = new Font ("Tahoma", 10, FontStyle.Bold);

			//Draw Y grids.
			DrawGrid (graphics);
			graphics.DrawRectangle (rectPen, rectBoundary);

			int i;
			double j = MinValue;
			for ( i = 0; i < histogram.Length; i++, j += Interval )
			{
				rectBin = new Rectangle ((int)( border + ( i * binWidth ) ) + rectBoundary.X,
				(int)( rectBoundary.Height - histogram[i] * yUnit ) + rectBoundary.Y,
				(int)binWidth, (int)( histogram[i] * yUnit ));

				//Draw bins Of Histogram.
				DrawBin (rectBin, graphics);

				//if ( border != 0 )
				//{
				//  //Display number of samples in each bin.
				//   SizeF sizef = g.MeasureString (histogram[i].ToString (), font);
				//   PointF pointf1 = new PointF ((float)( rectBin.X ),
				//      ( rectBin.Y - 15 + sizef.Height ));

				//   Matrix matrixSample = new Matrix ();

				//   matrixSample.RotateAt (-90.0f, pointf1);
				//   g.Transform = matrixSample;
				//   g.TranslateTransform (0, sizef.Height);
				//   g.DrawString (histogram[i].ToString (), font, Brushes.Black,
				//      ( rectBin.X ), ( rectBin.Y - 15 ));

				//   g.ResetTransform ();

				//}

				//Display Intervals on X Axis.
				DisplayIntervals (graphics, i, j);

			}

			//Display Last Interval.
			if ( i != 0 && j != 0 )
				DisplayIntervals (graphics, i, j);


			//Display X-Axis label.
			SizeF xtextSize = graphics.MeasureString (Xtext, fontTahoma);
			graphics.DrawString (Xtext, fontTahoma, Brushes.Black,
				 ( this.Width / 2 ) - 10 - xtextSize.Width, ( this.Height - xtextSize.Height ));

			//Display Y-Axis label.
			SizeF ytextSize = graphics.MeasureString (Ytext, fontTahoma);

			Matrix matrixYLabel = new Matrix ();

			PointF point = new PointF (5.0f, (float)( rectBoundary.Height / 2 + ytextSize.Width ));

			matrixYLabel.RotateAt (270.0f, point);
			graphics.Transform = matrixYLabel;
			graphics.DrawString (Ytext, fontTahoma, Brushes.Black, point);
			graphics.ResetTransform ();

			if ( this.Title != null )
			{
				//Display Title.
				Brush brushTitle = new SolidBrush (Color.FromArgb (97, 45, 38));
				SizeF textSize = graphics.MeasureString (text, fontTahoma);
				RectangleF rectanglef = new RectangleF (rectBoundary.X,
					 rectBoundary.Y - ( textSize.Height + 5 ),
					 textSize.Width + 4, textSize.Height + 4);

				graphics.FillRectangle (Brushes.LightYellow, rectanglef);
				graphics.DrawRectangle (Pens.LightGray, rectBoundary.X,
					 rectBoundary.Y - ( textSize.Height + 5 ),
					 textSize.Width + 4, textSize.Height + 4);

				rectanglef.Inflate (-2, -2);
				graphics.DrawString (text, fontTahoma, brushTitle, rectanglef);

			}
		}

		/// <summary>
		/// Overriden this method so that the graph area automatically resizes itself when
		/// the control is resized
		/// </summary>
		/// <param name="e"> Not used </param>
		protected override void OnResize ( EventArgs e )
		{
			base.OnResize (e);
			rectBoundary = new Rectangle (50, 60, this.Width - 100, this.Height - 120);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"></see> event.
		/// </summary>
		/// <param name="e">used for finding mouse location</param>			

		int intCurrBin = -1;

		protected override void OnMouseMove ( MouseEventArgs e )
		{
			base.OnMouseMove (e);

			double j = MinValue;
			int i;
			bool blnShowTooltip = false;
			for ( i = 0; i < histogram.Length; i++, j += Interval )
			{
				rectBin = new Rectangle ((int)( border + ( i * binWidth ) ) + rectBoundary.X,
				(int)( rectBoundary.Height - histogram[i] * yUnit ) + rectBoundary.Y, (int)binWidth,
				(int)( histogram[i] * yUnit ));


				if ( rectBin.Contains (e.Location) )
				{
					if ( i != intCurrBin )
					{
						toolTip1.ToolTipTitle = string.Format ("Samples in Interval {0:0.00} - {1:0.00}:",
							j, ( j += Interval ));
						toolTip1.IsBalloon = true;
						toolTip1.SetToolTip (this, histogram[i].ToString ());

					}
					//Debug.Print ("i: " + i.ToString () + "cb:" + currbin.ToString ());
					intCurrBin = i;
					blnShowTooltip = true;
					break;
				}
			}
			if ( blnShowTooltip == false )
			{
				intCurrBin = -1;
			}
		}

		#endregion

		#region Private Methods
		/// <summary>
		/// Draw bins of HistogramControl.
		/// </summary>
		/// <param name="pRect">Rectangle used for drawing Bins of Histogram</param>
		/// <param name="pGraphics ">graphics objects to render</param>
		/// <returns> void</returns>
		private void DrawBin ( Rectangle pRect, Graphics pGraphics )
		{
			pRect.Inflate (-1, -1);
			if ( pRect.Height <= 0 || pRect.Width <= 0 )
				return;

			GraphicsPath path = new GraphicsPath ();

			path.AddRectangle (pRect);

			PathGradientBrush brush = new PathGradientBrush (path);
			LinearGradientBrush lb = new LinearGradientBrush (pRect, Color.FromArgb (192, color),
				Color.Empty, LinearGradientMode.Vertical);

			brush.CenterColor = color;

			Color[] surrColors = { Color.FromArgb (128, color) };
			brush.SurroundColors = surrColors;
			Pen pen = new Pen (Color.FromArgb (64, color));

			pGraphics.FillRectangle (Brushes.Black, pRect);
			pGraphics.FillRectangle (brush, pRect);
			pGraphics.FillRectangle (lb, pRect);
			pGraphics.DrawRectangle (Pens.Black, pRect);
			pGraphics.DrawRectangle (pen, pRect);

		}
		/// <summary>
		/// Method for drawing Y Grids.
		/// </summary>
		/// <param name="pGraphics">graphics objects to render</param>
		private void DrawGrid ( Graphics graphics )
		{
			bool blnBrush = false;

			if ( AutoCalculate )
				YgridHeight = BinMax / 5;

			int gridHeight = (int)( (float)YgridHeight * yUnit );

			if ( gridHeight == 0 )
				return;

			int i = 1;
			Brush brush1 = new SolidBrush (Color.FromArgb (227, 229, 231));
			Brush brush2 = new SolidBrush (Color.FromArgb (232, 235, 238));

			Pen pen = new Pen (Color.FromArgb (211, 211, 211));

			for ( int y = this.Height - 60 - gridHeight; y > 60; y -= gridHeight, ++i )
			{
				Brush b;
				if ( blnBrush )
					b = brush1;
				else
					b = brush2;
				blnBrush = !blnBrush;

				graphics.FillRectangle (b, 50, y, rectBoundary.Width, gridHeight);
				graphics.DrawRectangle (pen, 50, y, rectBoundary.Width, gridHeight);
				graphics.DrawLine (Pens.Black, 45, y, 50, y);

				SizeF size = graphics.MeasureString (( i * YgridHeight ).ToString (), font);
				graphics.DrawString (( i * YgridHeight ).ToString (), font, Brushes.Black,
					rectBoundary.X - 5 - size.Width, y + 8 - size.Height);
			}

		}
		/// <summary>
		/// Display Intervals On X Axis.
		/// </summary>
		/// <param name="pGraphics">graphics objects to render</param>
		/// <param name="i">stores histogram array index value</param>
		/// <param name="j">stores Interval value</param>
		private void DisplayIntervals ( Graphics graphics, int i, double j )
		{
			graphics.DrawLine (Pens.Black, (int)( border + ( i * binWidth ) ) + rectBoundary.X,
								  (int)( rectBoundary.Height + rectBoundary.Y ),
								  (int)( border + ( i * binWidth ) ) + rectBoundary.X,
								  (int)( rectBoundary.Height + rectBoundary.Y ) + 5);

			SizeF sizeInterval = graphics.MeasureString (String.Format ("{0:0.00}", j), font);
			PointF pointInterval =
				new PointF ((float)( ( border + ( i * binWidth ) ) + rectBoundary.X - sizeInterval.Width ),
				(float)( ( rectBoundary.Height + rectBoundary.Y + 8 ) ));

			Matrix matrixInterval = new Matrix ();

			matrixInterval.RotateAt (-90.0f, new PointF (pointInterval.X + sizeInterval.Width,
				pointInterval.Y));
			graphics.Transform = matrixInterval;
			graphics.TranslateTransform (0, -sizeInterval.Height / 2);
			graphics.DrawString (String.Format ("{0:0.00}", j), font, Brushes.Black,
				pointInterval);
			graphics.ResetTransform ();
		}
		/// <summary>
		/// Method For Calculating Histogram.
		/// </summary>
		private void CalculateHistogram ( )
		{

			if ( listPlotData.Count < 2 )
				return;

			listPlotData.Sort ();
			histogram = new int[binCount];


			double dblMaxValue = listPlotData[listPlotData.Count - 1];

			MinValue = listPlotData[0];
			Interval = ( dblMaxValue - MinValue ) / binCount;

			//Debug.Print ("Interval = " + dblInterval.ToString ());

			for ( int j = 0; j < binCount; ++j )
			{
				int count = 0;
				for ( int i = 0; i < listPlotData.Count; ++i )
				{
					double min = MinValue + j * Interval;
					double max = min + Interval;

					if ( j == binCount - 1 )
					{
						if ( listPlotData[i] >= min && listPlotData[i] <= dblMaxValue )
						{
							count++;
						}
					}
					else
					{
						if ( listPlotData[i] >= min && listPlotData[i] < max )
						{
							count++;
						}
					}
				}
				histogram[j] = count;
			}

			//Calculate binWidth and yUnit.
			binWidth = (float)( ( rectBoundary.Width - 2 * border ) / histogram.Length );
			BinMax = Getmaxvalue (histogram);
			yUnit = ( (float)( rectBoundary.Height - 2 * border ) / (float)BinMax );

			return;
		}

		/// <summary>
		/// Method for finding maximum value element in an Array. 
		/// </summary>
		/// <param name="pArray">Array of integers</param>
		/// <returns>int</returns>
		private int Getmaxvalue ( int[] Array )
		{
			if ( IsDrawing )
			{
				int max = 0;
				for ( int i = 0; i < Array.Length; i++ )
				{
					if ( Array[i] > max )
						max = Array[i];
				}
				return max;
			}
			return 1;
		}
		#endregion

	}
}
